package modules

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"mime"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"time"
)

// sendSSEEvent 发送 SSE 事件给客户端
// 此函数用于远程下载文件时，实时返回下载进度
func sendSSEEvent(w http.ResponseWriter, flusher http.Flusher, success bool, progress float64, totalSize int64, downloadedSize int64, message string) {
	data := map[string]interface{}{
		"success":        success,
		"progress":       progress,
		"totalSize":      totalSize,
		"downloadedSize": downloadedSize,
		"message":        message,
	}
	jsonData, err := json.Marshal(data)
	if err != nil {
		log.Printf("JSON 编码出错: %v", err)
		return
	}
	fmt.Fprintf(w, "data: %s\n\n", jsonData)
	flusher.Flush()
}

// GetFileInfoFromURL 处理获取 http/https 地址文件信息的请求
func GetFileInfoFromURL(w http.ResponseWriter, r *http.Request) {
	PrintRequestInfo(r)

	// 解析查询参数
	if err := r.ParseForm(); err != nil {
		log.Printf("解析表单错误: %v", err)
		EncodeResponse(w, nil, "解析表单错误", http.StatusBadRequest)
		return
	}

	// 获取请求的 URL
	downloadURL := r.FormValue("url")
	downloadURL, _ = url.QueryUnescape(downloadURL)
	if downloadURL == "" {
		log.Println("未提供文件 URL")
		EncodeResponse(w, nil, "未提供文件 URL", http.StatusBadRequest)
		return
	}

	log.Printf("获取 URL 文件信息: %s", downloadURL)

	// 检查 URL 是否为 http/https
	if !strings.HasPrefix(downloadURL, "http://") && !strings.HasPrefix(downloadURL, "https://") {
		log.Println("不支持的 URL 协议，仅支持 http/https")
		EncodeResponse(w, nil, "不支持的 URL 协议，仅支持 http/https", http.StatusBadRequest)
		return
	}

	// 创建一个忽略 SSL 校验的 HTTP 客户端，并处理重定向
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{
		Transport: tr,
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			downloadURL = req.URL.String()
			log.Printf("重定向到: %s", downloadURL)
			return nil
		},
	}

	// 发送 HEAD 请求获取文件信息
	resp, err := client.Head(downloadURL)
	if err != nil {
		log.Printf("HEAD 请求失败，将用 GET 重试: %v", err)
		// 尝试使用 GET 请求获取文件信息
		resp, err = client.Get(downloadURL)
		if err != nil {
			msg := fmt.Sprintf("HEAD 和 GET 请求均获取文件信息失败: %v", err)
			log.Printf(msg)
			EncodeResponse(w, nil, msg, http.StatusBadRequest)
			return
		}
		// 关闭响应体以避免下载数据
		defer resp.Body.Close()
	}
	defer resp.Body.Close()

	// 检查响应状态码
	if resp.StatusCode != http.StatusOK {
		log.Printf("获取文件信息失败，状态码: %d", resp.StatusCode)
		EncodeResponse(w, nil, fmt.Sprintf("获取文件信息失败，状态码: %d", resp.StatusCode), http.StatusBadRequest)
		return
	}

	// 尝试从响应头中获取文件名
	filename := ""
	contentDisposition := resp.Header.Get("Content-Disposition")
	if contentDisposition != "" {
		// 解析 Content-Disposition 头
		_, params, err := mime.ParseMediaType(contentDisposition)
		if err == nil {
			if name, ok := params["filename"]; ok {
				filename = name
			}
		}
	}

	// 如果响应头中没有文件名，从 URL 中提取
	if filename == "" {
		parsedURL, err := url.Parse(downloadURL)
		if err == nil {
			filename = filepath.Base(parsedURL.Path)
		}
	}

	// 构建响应数据
	response := map[string]interface{}{
		"real_url": downloadURL,
		"filename": filename,
		"size":     resp.ContentLength,
	}

	// 返回响应
	EncodeResponse(w, response, "", http.StatusOK)
}

// 从指定 URL 下载文件到指定目录
func UploadFromURL(w http.ResponseWriter, r *http.Request) {
	// 设置 SSE 响应头
	w.Header().Set("Content-Type", "text/event-stream")
	w.Header().Set("Cache-Control", "no-cache")
	w.Header().Set("Connection", "keep-alive")

	// 处理客户端断开连接
	flusher, ok := w.(http.Flusher)
	if !ok {
		http.Error(w, "Streaming unsupported", http.StatusInternalServerError)
		return
	}

	PrintRequestInfo(r)
	if err := r.ParseForm(); err != nil {
		log.Printf("解析表单错误: %v", err)
		// 修改此处，补充 totalSize 和 downloadedSize 参数
		sendSSEEvent(w, flusher, false, 0, 0, 0, "解析表单错误")
		return
	}

	// 获取下载 URL
	downloadURL := r.FormValue("url")
	downloadURL, _ = url.QueryUnescape(downloadURL)
	if downloadURL == "" {
		log.Println("未提供上传 URL")
		// 修改此处，补充 totalSize 和 downloadedSize 参数
		sendSSEEvent(w, flusher, false, 0, 0, 0, "未提供上传 URL")
		return
	}

	newFilename := r.FormValue("newFilename")
	if newFilename == "" {
		log.Println("未提供新文件名")
		sendSSEEvent(w, flusher, false, 0, 0, 0, "未提供新文件名")
		return
	}
	// 获取新文件名
	if !IsFileAllowed(newFilename) {
		log.Printf("不支持的文件类型: %s", newFilename)
		sendSSEEvent(w, flusher, false, 0, 0, 0, "文件类型不支持")
		return
	}

	// 获取保存目录
	saveDir := r.FormValue("dir")
	saveDir, _ = url.QueryUnescape(saveDir)
	if saveDir == "" {
		log.Println("未提供保存目录")
		sendSSEEvent(w, flusher, false, 0, 0, 0, "未提供保存目录")
		return
	}

	// 解析保存目录
	parts := strings.SplitN(saveDir, "/", 2)
	var rootName, subPath string
	if len(parts) == 1 {
		rootName = parts[0]
		subPath = ""
	} else {
		rootName = parts[0]
		subPath = parts[1]
	}
	if _, exists := rootNames[rootName]; !exists {
		log.Printf("指定的根目录名 %s 不存在", rootName)
		sendSSEEvent(w, flusher, false, 0, 0, 0, "根目录不存在")
		return
	}

	// 构建保存路径
	saveFilename := filepath.Join(rootNames[rootName], subPath, newFilename)
	absSaveFilename, _ := filepath.Abs(saveFilename)
	if IsPathOutOfScope(absSaveFilename, rootName, nil) {
		sendSSEEvent(w, flusher, false, 0, 0, 0, "路径越权")
		return
	}

	// 检查文件是否已存在
	if _, err := os.Stat(saveFilename); !os.IsNotExist(err) {
		log.Println("文件已经存在")
		sendSSEEvent(w, flusher, false, 0, 0, 0, "文件已存在")
		return
	}

	// 检查 absSaveFilename 文件所在的目录是否存在，如果不存在则创建
	if !CreateDirectory(filepath.Dir(absSaveFilename), nil) {
		sendSSEEvent(w, flusher, false, 0, 0, 0, "创建目录失败")
		return
	}

	// 处理 http/https 下载
	if strings.HasPrefix(downloadURL, "http://") || strings.HasPrefix(downloadURL, "https://") {
		// 创建一个忽略 SSL 校验的 HTTP 客户端
		tr := &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		}
		client := &http.Client{Transport: tr}

		resp, err := client.Get(downloadURL)
		if err != nil {
			log.Printf("下载文件失败: %v", err)
			// 修改此处，补充 totalSize 和 downloadedSize 参数
			sendSSEEvent(w, flusher, false, 0, 0, 0, fmt.Sprintf("下载文件失败: %v", err))
			return
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusOK {
			log.Printf("下载文件失败，状态码: %d", resp.StatusCode)
			// 修改此处，补充 totalSize 和 downloadedSize 参数
			sendSSEEvent(w, flusher, false, 0, 0, 0, fmt.Sprintf("下载文件失败，状态码: %d", resp.StatusCode))
			return
		}

		outFile, err := os.Create(saveFilename)
		if err != nil {
			log.Printf("创建文件失败: %v", err)
			sendSSEEvent(w, flusher, false, 0, 0, 0, "创建文件失败")
			return
		}
		defer outFile.Close()

		// 获取文件总大小
		totalSize := resp.ContentLength
		if totalSize == -1 {
			log.Println("无法获取文件大小，无法显示进度")
			// 不支持进度显示，直接复制文件
			if _, err := io.Copy(outFile, resp.Body); err != nil {
				outFile.Close()
				if err := os.Remove(saveFilename); err != nil {
					log.Printf("删除未成功下载的文件失败: %v", err)
				}
				log.Printf("保存下载文件时出错: %v", err)
				sendSSEEvent(w, flusher, false, 0, 0, 0, "文件保存失败")
				return
			}
			sendSSEEvent(w, flusher, true, 100, 0, 0, "文件下载成功")
		} else {
			// 支持进度显示
			copied := int64(0)
			buf := make([]byte, 1024*1024) // 1024KB 缓冲区
			failCount := 0
			for {
				n, err := resp.Body.Read(buf)
				if n > 0 {
					if _, err := outFile.Write(buf[:n]); err != nil {
						log.Printf("保存下载文件时出错: %v", err)
						sendSSEEvent(w, flusher, false, 0, totalSize, copied, "文件保存失败")
						return
					}
					outFile.Sync()
					copied += int64(n)
					progress := float64(copied) / float64(totalSize) * 100
					sendSSEEvent(w, flusher, true, progress, totalSize, copied, "")
				}
				if err != nil {
					if err == io.EOF {
						break
					}
					// 如果失败，重试 3 次
					if failCount < 3 {
						failCount++
						log.Printf("读取下载文件时出错: %v，重试次数: %d", err, failCount)
						time.Sleep(1 * time.Second) // 等待 1 秒后重试
						continue
					}

					// 删除未成功下载的文件
					outFile.Close()
					if err := os.Remove(saveFilename); err != nil {
						log.Printf("删除未成功下载的文件失败: %v", err)
					}
					log.Printf("读取下载文件时出错: %v", err)
					sendSSEEvent(w, flusher, false, 0, totalSize, copied, "读取下载文件时出错")
					return
				}
			}
			sendSSEEvent(w, flusher, true, 100, totalSize, copied, "文件下载成功")
		}

		log.Printf("文件 %s 从 %s 下载成功，保存路径: %s", filepath.Base(downloadURL), downloadURL, saveFilename)
		return
	}

	// TODO 处理 ftp/ftps 下载支持

	log.Println("不支持的 URL 协议")
	sendSSEEvent(w, flusher, false, 0, 0, 0, "不支持的 URL 协议")
}
